package com.app.art.utils.jwt;

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;

import javax.crypto.SecretKey;
import java.time.Instant;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/**
 * JwtToken生成的工具类
 * JWT token的格式：header.payload.signature
 * header的格式（算法、token的类型）：
 * {"alg": "HS512","typ": "JWT"}
 * payload的格式（用户名、创建时间、生成时间）：
 * {"sub":"wang","created":1489079981393,"exp":1489684781}
 * signature的生成算法：
 * HMACSHA512(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)
 * Created by macro on 2018/4/26.
 */


public class JwtTokenUtil {
    private static final Logger LOGGER = LoggerFactory.getLogger(JwtTokenUtil.class);

    // Access Token 有效期（短时效）
    private static final long ACCESS_TOKEN_EXPIRATION_MS = 10 * 1000; // 15分钟

    // Refresh Token 有效期（长时效）
    private static final long REFRESH_TOKEN_EXPIRATION_MS = 7 * 24 * 60 * 60 * 1000; // 7天

    // 令牌黑名单（用于吊销，生产环境建议用 Redis）
    private final Map<String, Long> tokenBlacklist = new ConcurrentHashMap<>();

    private final SecretKey secretKey;

    public JwtTokenUtil(String secret) {
        this.secretKey = Keys.hmacShaKeyFor(secret.getBytes());
    }

    public String generateAccessToken(String username) {
        return generateAccessToken(username, new HashMap<>(0));
    }

    /**
     * 生成 Access Token
     */
    public String generateAccessToken(String username, Map<String, Object> claims) {
        return Jwts.builder().subject(username) // 主题
                .claims(claims) // 自定义声明
                .id(UUID.randomUUID().toString()) // 唯一标识符，用于防重放
                .issuedAt(Date.from(Instant.now())) // 签发时间
                .expiration(new Date(System.currentTimeMillis() + ACCESS_TOKEN_EXPIRATION_MS)) // 过期时间
                .signWith(secretKey, Jwts.SIG.HS256) // 强制指定算法
                .compact();
    }

    /**
     * 生成 Refresh Token
     */
    public String generateRefreshToken(String username) {
        return Jwts.builder()
                .subject(username)
                .claim("token_type", "refresh") // 明确令牌类型
                .id(UUID.randomUUID().toString())
                .issuedAt(Date.from(Instant.now()))
                .expiration(new Date(System.currentTimeMillis() + REFRESH_TOKEN_EXPIRATION_MS))
                .signWith(secretKey, Jwts.SIG.HS256)
                .compact();
    }

    /**
     * 验证并解析 Token（包含完整的安全验证）
     */
    public JwtClaims validateAndParseToken(String token) throws JwtException {
        try {
            Claims claims = Jwts.parser()
                    .verifyWith(secretKey)
                    .build()
                    .parseSignedClaims(token)
                    .getPayload();

            // 1. 检查是否在黑名单中
            if (tokenBlacklist.containsKey(claims.getId())) {
                throw new JwtException("Token has been revoked");
            }
            // 2. 验证标准声明
            validateClaims(claims);

            return new JwtClaims(claims.getSubject(), claims.getIssuedAt(), claims.getExpiration(), claims);
        } catch (ExpiredJwtException e) {
            throw new JwtException("Token has expired", e);
        } catch (UnsupportedJwtException e) {
            throw new JwtException("Unsupported token", e);
        } catch (MalformedJwtException e) {
            throw new JwtException("Malformed token", e);
        } catch (SignatureException e) {
            throw new JwtException("Invalid signature", e);
        } catch (IllegalArgumentException e) {
            throw new JwtException("Invalid token", e);
        }
    }

    /**
     * 验证所有必需的声明
     */
    private void validateClaims(Claims claims) {
        Date now = new Date();

        // 验证过期时间
        if (claims.getExpiration() == null || claims.getExpiration().before(now)) {
            throw new JwtException("Token is expired");
        }

        // 验证生效时间（如果有）
        if (claims.getNotBefore() != null && claims.getNotBefore().after(now)) {
            throw new JwtException("Token is not yet valid");
        }

        // 验证签发时间（可选但推荐）
        if (claims.getIssuedAt() == null) {
            throw new JwtException("Token missing issued at claim");
        }

        // 验证受众（如果应用需要）
        // if (claims.getAudience() == null || !claims.getAudience().contains("your-audience")) {
        //     throw new JwtException("Invalid audience");
        // }
    }

    /**
     * 吊销令牌（加入黑名单）
     */
    public void revokeToken(String token) {
        try {
            Claims claims = Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload();

            // 将令牌ID加入黑名单，有效期到令牌自然过期
            long expirationTime = claims.getExpiration().getTime();
            tokenBlacklist.put(claims.getId(), expirationTime);

            // 定期清理过期黑名单条目（生产环境建议用 Redis TTL）
            cleanupBlacklist();

        } catch (Exception e) {
            throw new JwtException("Failed to revoke token", e);
        }
    }

    /**
     * 清理过期的黑名单条目
     */
    private void cleanupBlacklist() {
        long now = System.currentTimeMillis();
        tokenBlacklist.entrySet().removeIf(entry -> entry.getValue() < now);
    }

    /**
     * 使用 Refresh Token 获取新的 Access Token
     */
    public String refreshAccessToken(String refreshToken) {
        JwtClaims claims = validateAndParseToken(refreshToken);

        // 验证这是 Refresh Token
        if (!"refresh".equals(claims.getAdditionalClaims().get("token_type"))) {
            throw new JwtException("Invalid refresh token");
        }

        // 吊销旧的 refresh token（可选，实现刷新令牌轮换）
//        revokeToken(refreshToken);

        // 生成新的令牌对
        Map<String, Object> newClaims = new HashMap<>();
        // 复制需要的声明
        claims.getAdditionalClaims().forEach((key, value) -> {
            if (!"token_type".equals(key) && !"exp".equals(key) && !"iat".equals(key)) {
                newClaims.put(key, value);
            }
        });

        return generateAccessToken(claims.getUsername(), newClaims);
    }

    /**
     * 从token中获取JWT中的负载
     */


    /**
     * 生成token的过期时间
     */


    /**
     * 从token中获取登录用户名
     */


    /**
     * 验证token是否还有效
     *
     * @param token    客户端传入的token
     * @param username 从数据库中查询出来的账号
     */


    /**
     * 判断token是否已经失效
     */


    /**
     * 从token中获取过期时间
     */


    /**
     * 根据用户信息生成token
     */


    /**
     * 当原来的token没过期时是可以刷新的
     *
     * @param oldToken 带tokenHead的token
     */


    /**
     * 判断token在指定时间内是否刚刚刷新过
     *
     * @param token 原token
     * @param time  指定时间（秒）
     */

}
